package com.cupdata.zicon.jdk7concurrent.chapter3;

import java.util.Date;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;

public class PhaserMyTest {
	public static void main(String[] args) {
		MyPhaser phaser = new MyPhaser();
		final int number = 5;
		Student students[] = new Student[number];
		/*
		 * In the Core class, when you created the MyPhaser object, you didn't
		 * specify the number of participants in the phaser. You made a call to
		 * the register() method for every Student object created to register a
		 * participant in the phaser. This calling doesn't establish a relation
		 * between the Student object or the thread that executes it and the
		 * phaser. Really, the number of participants in a phaser is only a
		 * number. There is no relationship between the phaser and the
		 * participants.
		 */
		for (int i = 0; i < students.length; i++) {
			students[i] = new Student(phaser);
			phaser.register();
		}

		Thread threads[] = new Thread[students.length];
		for (int i = 0; i < students.length; i++) {
			threads[i] = new Thread(students[i], "Student " + i);
			threads[i].start();
		}

		for (int i = 0; i < threads.length; i++) {
			try {
				threads[i].join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		System.out.printf("Main: The phaser has finished: %s.\n",
				phaser.isTerminated());
	}
}

class MyPhaser extends Phaser {
	/**
	 * This method is called by the phaser before making a phase change and
	 * before waking up all the threads that were sleeping in the
	 * arriveAndAwaitAdvance() method. This method receives as parameters the
	 * number of the actual phase, where 0 is the number of the first phase and
	 * the number of registered participants. The most useful parameter is the
	 * actual phase. If you execute a different operation depending on the
	 * actual phase, you have to use an alternative structure (if/else or
	 * switch) to select the operation you want to execute. In the example, we
	 * used a switch structure to select a different method for each change of
	 * phase.
	 * 
	 * The onAdvance() method returns a Boolean value that indicates if the
	 * phaser has terminated or not.
	 */
	@Override
	protected boolean onAdvance(int phase, int registeredParties) {
		switch (phase) {
		case 0:
			return studentsArrived(); // 返回false
		case 1:
			return finishFirstExercise(); // 返回false
		case 2:
			return finishSecondExercise(); // 返回false
		case 3:
			return finishExam(); // 返回true
		default:
			return true;
		}
	}

	private boolean studentsArrived() {
		System.out
				.printf("Phaser: The exam are going to start. The students are ready.\n");
		System.out.printf("Phaser: We have %d students.\n",
				getRegisteredParties());
		return false;
	}

	private boolean finishFirstExercise() {
		System.out
				.printf("Phaser: All the students have finished the first exercise.\n");
		System.out.printf("Phaser: It's time for the second one.\n");
		return false;
	}

	private boolean finishSecondExercise() {
		System.out
				.printf("Phaser: All the students have finished the second exercise.\n");
		System.out.printf("Phaser: It's time for the third one.\n");
		return false;
	}

	private boolean finishExam() {
		System.out.printf("Phaser: All the students have finished the exam.\n");
		System.out.printf("Phaser: Thank you for your time.\n");
		return true;
	}
}

class Student implements Runnable {
	private Phaser phaser;

	public Student(Phaser phaser) {
		this.phaser = phaser;
	}

	@Override
	public void run() {
		System.out.printf("%s: Has arrived to do the exam.%s\n", Thread
				.currentThread().getName(), new Date());
		phaser.arriveAndAwaitAdvance();// phaser 0,返回false
		System.out.printf("%s: Is going to do the first exercise. %s\n", Thread
				.currentThread().getName(), new Date());
		doExercise1();
		System.out.printf("%s: Has done the first exercise. %s\n", Thread
				.currentThread().getName(), new Date());
		phaser.arriveAndAwaitAdvance();// //phaser 1,返回false
		System.out.printf("%s: Is going to do the second exercise.%s\n", Thread
				.currentThread().getName(), new Date());
		doExercise2();
		System.out.printf("%s: Has done the second exercise. %s\n", Thread
				.currentThread().getName(), new Date());
		phaser.arriveAndAwaitAdvance();// //phaser 2,返回false
		System.out.printf("%s: Is going to do the third exercise. %s\n", Thread
				.currentThread().getName(), new Date());
		doExercise3();
		System.out.printf("%s: Has finished the exam. %s\n", Thread
				.currentThread().getName(), new Date());
		phaser.arriveAndAwaitAdvance();// //phaser 3 ,返回true
	}

	private void doExercise1() {
		try {
			long duration = (long) (Math.random() * 10);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void doExercise2() {
		try {
			long duration = (long) (Math.random() * 10);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private void doExercise3() {
		try {
			long duration = (long) (Math.random() * 10);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
